﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Diagnostics;

namespace SsTestHarness
{
    class Program
    {
        private const int TestFailure = -1;
        private const int TestSuccess = 0;

        static string deployDir;

        static int Main(string[] args)
        {
            //string s = Directory.GetCurrentDirectory();
            //Console.WriteLine(s);
            //Console.WriteLine(args[1]);
            //Console.ReadLine();

            deployDir = args[1];

            string trFilename = args[0];
            Stream trFile = File.OpenRead(trFilename);


            XmlSerializer trXS = new XmlSerializer(typeof(TestRun));
            TestRun testRun = (TestRun) trXS.Deserialize(trFile);

            SummaryResult sr = new SummaryResult();
            sr.TestName = testRun.TestName;
            sr.TestResult = testResultType.InProgress;
            sr.DetailedResultsFile = "ingen fil"; ;
            sr.InnerTests = new SummaryResultInnerTest[testRun.InnerTests.Length];

            try
            {
                int testNumber = 0;
                foreach (TestRunInnerTest test in testRun.InnerTests)
                {
                    SummaryResultInnerTest testSummary = new SummaryResultInnerTest();
                    sr.InnerTests[testNumber] = testSummary;
                    ExecuteTest(test, testSummary);
                    if (testSummary.TestResult != testResultType.Passed) sr.TestResult = testResultType.Failed;

                    Console.WriteLine("Test '{0}' exited with status '{1}'.", test.TestName, testSummary.TestResult.ToString());
                    testNumber++;
                }

                if (sr.TestResult != testResultType.Failed) sr.TestResult = testResultType.Passed;
                sr.ErrorMessage = (sr.TestResult == testResultType.Passed) ? "Test Passed." : "One or more tests failed.";

            }
            catch (Exception e)
            {
                sr.ErrorMessage = "Exception in test:\n" + e.ToString();
                sr.TestResult = testResultType.Error;
            }
            finally
            {
                XmlSerializer srXS = new XmlSerializer(typeof(SummaryResult));
                Stream srFile = File.Create(testRun.SummaryResultsFile);
                srXS.Serialize(srFile, sr);
            }

            return (sr.TestResult == testResultType.Passed) ? TestSuccess : TestFailure;
        }


        static StringBuilder sbStdOut;
        static StringBuilder sbStdErr;

        private static void ExecuteTest(TestRunInnerTest test, SummaryResultInnerTest testSummary)
        {

            StringBuilder sbResults = new StringBuilder();
            sbStdErr = new StringBuilder();
            sbStdOut = new StringBuilder();
            bool testFailed = false;

            try
            {
                testSummary.TestName = test.TestName;
                testSummary.DetailedResultsFile = test.DetailedResultsFile;

                sbResults.AppendLine("Summary Results for Test: " + test.TestName);
                sbResults.AppendLine("ExeFile: " + test.ExeFile);
                sbResults.AppendLine("Cmd-Line Args: " + test.CmdLineArgs);
     
                bool setDir = test.ExeFile.StartsWith(@".\");
              
                Process proc = new Process();
                proc.StartInfo.FileName = test.ExeFile;
                proc.StartInfo.Arguments = test.CmdLineArgs;
                proc.StartInfo.UseShellExecute = false;
                proc.StartInfo.RedirectStandardError = true;
                proc.StartInfo.RedirectStandardInput = true;
                proc.StartInfo.RedirectStandardOutput = true;

                //proc.EnableRaisingEvents = true;

                string oldDir = null;
                if (setDir)
                {
                    oldDir = Directory.GetCurrentDirectory();
                    Directory.SetCurrentDirectory(deployDir);
                }
                else proc.StartInfo.WorkingDirectory = deployDir;

                proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);
                proc.ErrorDataReceived += new DataReceivedEventHandler(proc_ErrorDataReceived);
                //proc.Exited += new EventHandler(proc_Exited);

                try
                {

                    proc.Start();

                    proc.BeginErrorReadLine();
                    proc.BeginOutputReadLine();

                    if (test.StdInFile != null)
                    {
                        StreamWriter stdIn = proc.StandardInput;
                        string inData = File.ReadAllText(Path.Combine(deployDir,test.StdInFile));
                        stdIn.Write(inData);
                        stdIn.Flush();
                    }

                    proc.WaitForExit();

                }
                finally
                {
                    if (setDir) Directory.SetCurrentDirectory(oldDir);
                }

                //Verify exit code
                sbResults.AppendLine("Expected Process Exit Code: " + test.ExitCode);
                sbResults.AppendLine("Actual Process Exit Code: " + proc.ExitCode);
                if (proc.ExitCode != test.ExitCode)
                {
                    sbResults.AppendLine("Test Failed to meet expected exit code.");
                    testFailed = true;
                }
                   
                //Verify StdErr
                sbResults.AppendLine("StdErr:");
                string stdErr = sbStdErr.ToString();
                sbResults.AppendLine(stdErr);
                if (test.StdErrCompareFile != null)
                {
                    string compare = File.ReadAllText(Path.Combine(deployDir, test.StdErrCompareFile));
                    if (String.CompareOrdinal(stdErr, compare) != 0)
                    {
                        sbResults.AppendLine("StdErr is not correct!");
                        testFailed = true;
                    }
                    else
                    {
                        sbResults.AppendLine("StdErr matches expected results!");
                    }
                    sbResults.AppendLine("Expected ordinal StdErr:");
                    sbResults.AppendLine(compare);
                }

                //Verify StdOut
                sbResults.AppendLine("StdOut:");
                string stdOut = sbStdOut.ToString();
                sbResults.AppendLine(stdOut);
                if (test.StdOutCompareFile != null)
                {
                    string compare = File.ReadAllText(Path.Combine(deployDir,test.StdOutCompareFile));
                    if (String.CompareOrdinal(stdOut, compare) != 0)
                    {
                        sbResults.AppendLine("StdOut is not correct!");
                        testFailed = true;
                    }
                    else
                    {
                        sbResults.AppendLine("StdOut matches expected results!");
                    }
                    sbResults.AppendLine("Expected ordinal StdOut:");
                    sbResults.AppendLine(compare);
                }

                testSummary.TestResult = testFailed ? testResultType.Failed : testResultType.Passed;
                testSummary.ErrorMessage = testFailed ? "Test Failed." : "Test Passed.";
            }
            catch (Exception e)
            {
                testSummary.ErrorMessage = "Exception in test.";
                sbResults.AppendLine("Exception occured:");
                sbResults.AppendLine(e.ToString());

                testSummary.TestResult = testResultType.Error;
            }
            finally
            {
                File.WriteAllText(test.DetailedResultsFile, sbResults.ToString());
            }

        }

        static void proc_Exited(object sender, EventArgs e)
        {
            Console.WriteLine("Exited.");
        }

        static void proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            sbStdErr.AppendLine(e.Data);
        }

        static void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            sbStdOut.AppendLine(e.Data);
        }
    }
}
